package owlapps2012.owlToSql.events.p4;

import uk.ac.manchester.cs.jfact.JFactFactory;
import java.io.File;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.log4j.Logger;
import org.coode.owlapi.manchesterowlsyntax.ManchesterOWLSyntaxEditorParser;
import org.semanticweb.owlapi.apibinding.OWLManager;
import org.semanticweb.owlapi.expression.ParserException;
import org.semanticweb.owlapi.model.AddAxiom;
import org.semanticweb.owlapi.model.IRI;
import org.semanticweb.owlapi.model.OWLClass;
import org.semanticweb.owlapi.model.OWLClassExpression;
import org.semanticweb.owlapi.model.OWLDataFactory;
import org.semanticweb.owlapi.model.OWLOntology;
import org.semanticweb.owlapi.model.OWLOntologyCreationException;
import org.semanticweb.owlapi.model.OWLOntologyManager;
import org.semanticweb.owlapi.model.OWLOntologyStorageException;
import org.semanticweb.owlapi.reasoner.InferenceType;
import org.semanticweb.owlapi.reasoner.Node;
import org.semanticweb.owlapi.reasoner.NodeSet;
import org.semanticweb.owlapi.reasoner.OWLReasoner;
import org.semanticweb.owlapi.reasoner.OWLReasonerFactory;
import org.semanticweb.owlapi.util.SimpleShortFormProvider;
import owlapps2012.base.DLQueryParser;
import owlapps2012.base.RelationMapperConfig;
import uk.ac.manchester.cs.owl.owlapi.mansyntaxrenderer.ManchesterOWLSyntaxOWLObjectRendererImpl;

public class RelationMapperStandalone 
{
	/**
	 * 
	 */
	private static final String configFileName = RelationMapperStandalone.class.getSimpleName().concat(".properties");
	private static Logger log = Logger.getLogger(RelationMapperStandalone.class);
	private RelationMapperConfig relationMapperConfig;
	private Pattern pattern;
	private static final int limit = 100;

	public static void main(String[] args) throws Exception 
	{
		RelationMapperStandalone m = new RelationMapperStandalone();
		m.relationMapperConfig = new RelationMapperConfig("is_reported_with some {X}","medical_procedure_ICD9", true);

		m.map();
		
	}

	public void map() throws OWLOntologyCreationException, OWLOntologyStorageException 
	{
		String restr = ManchesterOWLSyntaxEditorParser.SOME;
		pattern = 			 Pattern.compile(relationMapperConfig.getBaseRelation().concat(" ("+restr+"|min [0-9]{1,}) ([a-zA-Z-_0-9\\.]{1,})"));
		OWLOntologyManager manager = OWLManager.createOWLOntologyManager();
		OWLOntology activeOntology =  manager.loadOntologyFromOntologyDocument(new File("f:/w/protege4dev/medical_facts_example_t.owl"));
		DLQueryParser dlqp = new DLQueryParser(activeOntology, new SimpleShortFormProvider());
		ManchesterOWLSyntaxOWLObjectRendererImpl renderer = new ManchesterOWLSyntaxOWLObjectRendererImpl();

        OWLDataFactory factory = manager.getOWLDataFactory();
		OWLReasonerFactory reasonerFactory = new JFactFactory();
        OWLReasoner reasoner = reasonerFactory.createReasoner(activeOntology);
        reasoner.precomputeInferences(new InferenceType[0]);
        
        OWLClass categoryToAnalyze = factory.getOWLClass(IRI.create("http://basic-ontology.googlecode.com/svn/trunk/medical_facts_example.owl#Procedura_1"));
        if (categoryToAnalyze != null ) 
        {
        
        	Set<OWLClassExpression> eces = categoryToAnalyze.getEquivalentClasses(activeOntology);
        	if(eces.size()>1)
        	{
        		log.warn("Transformation canceled - cause: more than one definition of category: "+categoryToAnalyze);
        		return;
        	}
        	
        	for(OWLClassExpression ece : eces)
        	{
        		String expressionToTransform = renderer.render(ece);
//        		System.out.println(" *** Analyzing category "+categoryToAnalyze+" with definition: "+s);
        		log.info(" *** Analyzing category "+categoryToAnalyze+" with definition: "+expressionToTransform);
        		String transformedExpression = expressionToTransform;
        		boolean isChanged = false;
        		Matcher m = pattern.matcher(transformedExpression);
        		int matchCount = 0;
        		int startFrom = 0;
        		while(m.find(startFrom))
        		{
        			matchCount++;
        			if(matchCount > limit)
        			{
        				log.warn("Limit exceeded! Probably a loop in definition of mappinngs.");
        				return;
        			}
        			
        			log.info("Found: "+m.group());
//        			String what = relationMapperConfig.isBaseRelationAnObjectProperty() ? m.group(2) : m.group(1);
        			String what = m.group(2);
        			OWLClassExpression mappingAxiom;
        			try 
        			{
        				mappingAxiom = dlqp.parseClassExpression(relationMapperConfig.getMappingAxiomTemplate().replaceFirst("\\{X\\}", what));
        			}
        			catch (ParserException e1) 
        			{
        				log.warn("Unable to parse expression: "+relationMapperConfig.getMappingAxiomTemplate().replaceFirst("\\{X\\}", what), e1);
        				return;
        			}
        			
        			NodeSet<OWLClass> subclsSet = reasoner.getSubClasses(mappingAxiom, relationMapperConfig.isOnlyDirectSubcategories());
        			if(!subclsSet.isEmpty())
        			{
        				String extExpression = "";
        				for(Node<OWLClass> scn : subclsSet.getNodes()) 
        				{
        					for (OWLClass sc : scn)  
        					{
        						if(!sc.isOWLNothing())
        						{
       								extExpression = extExpression.concat(" "+ManchesterOWLSyntaxEditorParser.OR+" ");
        							extExpression = extExpression.concat(relationMapperConfig.getBaseRelation().concat(" ").concat(restr).concat(" ").concat(renderer.render(sc)));
        							isChanged = true;
        						}
        					}
        				}
        				
        				if(isChanged)
        				{
        					extExpression = "(".concat(m.group()).concat(extExpression).concat(")");
        					transformedExpression = transformedExpression.substring(0, m.start()).concat(extExpression).concat(transformedExpression.substring(m.end()));
        				}
        			}
        			startFrom = (m.end());
        			m = pattern.matcher(transformedExpression);
        		}
        		
        		if(isChanged)
        		{
        			int i=0;
        			String newClassName;
        			do
        			{
        				i++;
        				newClassName = renderer.render(categoryToAnalyze).concat("_v"+i);
        			} while(activeOntology.containsClassInSignature(IRI.create(activeOntology.getOntologyID().getOntologyIRI()+"#"+newClassName)));
        			
        			OWLClass newClass = factory.getOWLClass(IRI.create(activeOntology.getOntologyID().getOntologyIRI()+"#"+newClassName));
        			OWLClassExpression newClassDef;
					try 
					{
						newClassDef = dlqp.parseClassExpression(transformedExpression);
					} 
					catch (ParserException e1) 
					{
						log.warn("Unable to parse expression: "+transformedExpression, e1);
        				return;
					}
					log.info("New definition: "+ renderer.render(newClassDef));
        			activeOntology.getOWLOntologyManager().applyChange(new AddAxiom(activeOntology, factory.getOWLEquivalentClassesAxiom(newClass, newClassDef)));
        			activeOntology.getOWLOntologyManager().applyChange(new AddAxiom(activeOntology, factory.getOWLSubClassOfAxiom(categoryToAnalyze, newClass)));
            		activeOntology.getOWLOntologyManager().applyChange(new AddAxiom(activeOntology, factory.getOWLAnnotationAssertionAxiom(newClass.getIRI(), factory.getOWLAnnotation(factory.getRDFSComment(), factory.getOWLLiteral("Added by Relation Mapper on "+new java.util.Date()+".")))));

        			log.info("New category "+newClass+" has been succesfully added.");
        			
        				activeOntology.getOWLOntologyManager().saveOntology(activeOntology);
        		}
        		else
        			log.info("No changes has been made.");
        	}
        	
        }
		
	}
	
}
